﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using blackhouse.BlackApart.Business.Apartments;
using blackhouse.BlackApart.Business.ApartmentServices;
using blackhouse.BlackApart.Business.Payments;
using blackhouse.BlackApart.Business.Prices;

namespace blackhouse.BlackApart.Business.Reservations {
    public class ImpWebReservation : IWebReservation {

        #region Fields

        private List<ApartmentWebReservation> apartmentReservation = new List<ApartmentWebReservation>();
        private List<ReservationPriceRabat> reservationRabats = new List<ReservationPriceRabat>();
        private TimeSpan arrivalTime = TimeSpan.FromHours(14);
        private double price;
        private float rabat;
        private double finalPrice;
        private bool calcFinalPrice;

        #endregion

        #region Methods

        public int ReservationId { get; set; }
        public string ReservationNumber { get; set; }
        public DateTime ArrivalDate { get; set; }
        public DateTime DepartureDate { get; set; }
        public TimeSpan ArrivalTime { get; set; }
        public double Price {
            get { return this.price; }
            set {
                if (this.price != value) {
                    this.price = value;
                    this.calcFinalPrice = false;
                }

            }
        }
        public double FinalPrice {
            get {
                if (!calcFinalPrice)
                    this.RecalculatePrices();
                return this.finalPrice;
            }
            private set { this.finalPrice = value; }
        }
        public float Rabat {
            get {
                if (!calcFinalPrice)
                    this.RecalculatePrices();
                return this.rabat;
            }
            private set { this.rabat = value; }
        }
        public int PersonCount {
            get { return this.ApartmentReservation.Sum(r => r.PeopleCount); }
        }
        public bool VatInvoice { get; set; }
        public ReservationStatus Status { get; set; }
        public Guid Guid { get; set; }
        public ApartmentWebReservation[] ApartmentReservation { get { return this.apartmentReservation.ToArray(); } }
        public ReservationPriceRabat[] ReservationRabats { get { return this.reservationRabats.ToArray(); } }
        public string OfficeComments { get; set; }
        public string Comments { get; set; }
        public int ClientId { get; set; }
        public int PaymentTypeId { get; set; }
        public IClient Client { get; set; }
        public IPaymentType PaymentType { get; set; }

        #endregion

        #region Methods

        public void SetClient(IClient client) {
            this.Client = client;
        }

        public void SetPaymentType(IPaymentType paymentType) {
            this.PaymentType = paymentType;
            ReservationPriceRabat paymentRabat = this.reservationRabats.FirstOrDefault(rr => rr.Rabat.RabatKey == "payment_type");
            if (paymentType.ExtraRabat == 0 && paymentRabat != null && paymentRabat.GetRabatValue() != 0)
                paymentRabat.OverrideRabatValue = 0;
            if (paymentType.ExtraRabat != 0) {
                if (paymentRabat == null) {
                    IPriceRabat priceRabat = Factory.Instance().GetPriceRabat("payment_type");
                    if (priceRabat == null)
                        throw new InvalidCastException("Brakuje rabatu o kluczu payment_type, przez co nie można obsłużyć dodatkowego rabatu za typ płatności!");
                    this.AddPriceRabat(priceRabat, Convert.ToSingle(paymentType.ExtraRabat));
                } else
                    paymentRabat.OverrideRabatValue = (float)paymentType.ExtraRabat;
            }
        }

        private void RecalculatePrices() {
            this.calcFinalPrice = true;
            this.Rabat = this.reservationRabats.Sum(r => r.GetRabatValue());
            this.FinalPrice = this.Price + this.Rabat / 100 * this.Price;
        }

        public void SetDates(DateTime arrivalDate, DateTime departureDate) {
            this.ArrivalDate = arrivalDate.Date + this.arrivalTime;
            this.DepartureDate = departureDate.Date + TimeSpan.FromHours(12);
        }

        public void SetArrivalTime(TimeSpan arrivalTime) {
            this.arrivalTime = arrivalTime;
            this.SetDates(this.ArrivalDate, this.DepartureDate);
        }

        public bool AddApartment(int apartmentId, int forPeoples) {
            IApartment apartment = Factory.Instance().GetApartment(apartmentId);
            if (apartment.Id != apartmentId) return false;
            if (apartment.Group.NumberOfPeople < forPeoples) return false;
            this.apartmentReservation.Add(new ApartmentWebReservation(this, apartmentId, forPeoples));
            return true;
        }

        public void AddApartment(ApartmentWebReservation apartment) {
            this.apartmentReservation.Add(apartment);
        }

        public void AddPriceRabat(IPriceRabat rabat) {
            this.reservationRabats.Add(new ReservationPriceRabat(rabat));
        }

        public void AddPriceRabat(IPriceRabat rabat, float overrideRabat) {
            ReservationPriceRabat rr = new ReservationPriceRabat(rabat);
            rr.OverrideRabatValue = overrideRabat;
            this.reservationRabats.Add(rr);
        }

        public void AddPriceRabat(ReservationPriceRabat rabat) {
            this.reservationRabats.Add(rabat);
        }

        public bool Save() {

            bool saveState = true;

            if (ArrivalDate == DateTime.MinValue || DepartureDate == DateTime.MinValue || ArrivalDate > DepartureDate)
                throw new ArgumentNullException("Dates isn't set.");

            saveState = this.apartmentReservation.Count > 0;

            if (!saveState) return false;

            return true;
        }

        public void SetPrice(IApartmentPrice price) {
            this.Price = price.GetPriceWithoutRabat();
            this.AddApartmentsRabatFromSetPrice(price);
        }

        public void AddApartmentsRabatFromSetPrice(IApartmentPrice price) {
            foreach (IPriceRabat rabat in price.GetAssociacedRabats()) {
                if (rabat.RabatHasBeenOverridden)
                    this.AddPriceRabat(rabat, rabat.RabatValue);
                else
                    this.AddPriceRabat(rabat);
            }
            //float rv = price.GetRabat();
            //if (rv == 0) return;

            //IPriceRabat r = Factory.Instance().GetPriceRabat("period");
            //if (r != null) {
            //    this.AddPriceRabat(r, rv);
            //}
            //else throw new ExceptionAparts(6, null);
        }

        public void ChangeStatus(string newStatusKey) {

        }

        public void Remove() {
            this.apartmentReservation.Clear();
            this.Client = null;
            this.ClientId = 0;
            this.Comments = "";
            this.ReservationId = 0;
            this.ReservationNumber = String.Empty;
            this.reservationRabats.Clear();
            this.calcFinalPrice = false;
        }

        public void RemoveOpenReservation() { }

        #endregion

    }
}
